/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package cageballserver.match;

import cageballcommon.exceptions.CageballIOException;
import cageballcommon.messages.Action;
import cageballcommon.enummerations.TeamEnum;
import cageballcommon.exceptions.CageballException;
import cageballcommon.exceptions.CageballMatchException;
import cageballcommon.messages.Info;
import cageballcommon.messages.State;
import cageballserver.CageballClient;
import cageballserver.Game;
import cageballserver.GameType;
import cageballserver.io.ClientState;
import cageballserver.io.GameServer;
import cageballserver.io.ObserverServer;
import cageballserver.physics.PhysicsEngine;
import cageballserver.scoreboard.ScoreBoardTournament;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author kanonas
 */
public class Match {

    private PhysicsEngine engine;
    private CageballClient team1;
    private CageballClient team2;
    private Collection<CageballClient> clients;
    private int team1Score;
    private int team2Score;
    private float fieldLengthX;
    private Inverter inverter;
    private ObserverServer observerServer;
    private long matchLength;
    private int stateDelay;
    private final GameServer gameServer;
    private final Properties gameProperties;
    private boolean running;
    private GameType gameType;
    private TeamEnum winner;
    ScoreBoardTournament scoreBoard;

    public Match(Collection<CageballClient> clients, Properties gameProperties, PhysicsEngine engine, ObserverServer observerServer, GameServer gameServer, GameType gameType) throws CageballIOException {

        this.clients = clients;
        this.observerServer = observerServer;
        this.gameServer = gameServer;
        this.engine = engine;
        this.gameProperties = gameProperties;
        this.gameType = gameType;
        team1Score = 0;
        team2Score = 0;
        inverter = new Inverter(gameProperties);
        running = true;
        winner = TeamEnum.none;

        scoreBoard = new ScoreBoardTournament(clients);
    }

    public void playMatch() throws CageballMatchException {

        ArrayList<CageballClient> list = new ArrayList<CageballClient>(clients);

        switch (gameType) {
            case Single:
                if (list.size() == 2) {
                    team1 = list.get(0);
                    team2 = list.get(1);

                    startMatch(new Game(team1, team2));
                } else {
                    throw new CageballMatchException("Number of teams has to be 2");
                }
                break;

            case Tournament:

                ArrayList<Game> matches = new ArrayList();

                for (int i = 0; i < list.size()-1; i++) {
                    for (int j = i+1; j < list.size(); j++) {
                        matches.add(new Game(list.get(i), list.get(j)));
                    }
                }

                Collections.shuffle(matches);

                for (Game game : matches) {
                    team1 = game.getTeam1();
                    team2 = game.getTeam2();

                    startMatch(game);
                }

                break;
            case PlayOff:



                break;
            default:

        }
    }

    private void startMatch(Game game) {

        State state;
        Action action;
        Info info;
        TeamEnum goal;

        engine.init(gameProperties, team1.getInitTeam(), inverter.invertInitTeam(team2.getInitTeam()));
        fieldLengthX = Float.parseFloat(gameProperties.getProperty("fieldLengthX"));
        matchLength = Long.parseLong(gameProperties.getProperty("matchLength"));
        stateDelay = Integer.parseInt(gameProperties.getProperty("stateDelay"));
        observerServer.init(gameProperties, team1.getInitTeam(), inverter.invertInitTeam(team2.getInitTeam()));
        try {
            team1.writeInitTeam(inverter.invertInitTeam(team2.getInitTeam()));
        } catch (CageballIOException ex) {
            gameServer.removeClient(team1);
            return;
        }

        try {
            team2.writeInitTeam(inverter.invertInitTeam(team1.getInitTeam()));
        } catch (CageballIOException ex) {
            gameServer.removeClient(team2);
            return;
        }

        while (running) {

            state = engine.getState();

            goal = checkGoal(state);

            if (goal != TeamEnum.none) {

                engine.reset(goal);
                state = engine.getState();

                info = new Info();
                info.setGoal(goal);

                state.setInfo(info);

                if (goal == TeamEnum.myTeam) {
                    team1Score++;
                } else {
                    team2Score++;
                }
            }

            if (state.getTime() >= matchLength) {
                endMatch(getLeader(), game);
                return;
            }

            sendState(state, game);
            try {
                action = team1.readAction();
                engine.addAction(action);
            } catch (CageballException ex) {
                gameServer.removeClient(team1);
                endMatch(TeamEnum.otherTeam, game);
                return;
            }
            try {
                action = team2.readAction();
                engine.addAction(inverter.invertAction(action));
            } catch (CageballException ex) {
                gameServer.removeClient(team2);
                endMatch(TeamEnum.myTeam, game);
                return;
            }

            engine.step();
//            try {
//                Thread.sleep(stateDelay);
//            } catch (InterruptedException ex) {
//                Logger.getLogger(Match.class.getName()).log(Level.SEVERE, null, ex);
//            }
        }
    }

    private TeamEnum checkGoal(State state) {
        if (state.getBall().getX() > fieldLengthX) {
            return TeamEnum.myTeam;
        } else if (state.getBall().getX() < 0) {
            return TeamEnum.otherTeam;
        } else {
            return TeamEnum.none;
        }
    }

    private TeamEnum getLeader() {
        if (team1Score > team2Score) {
            return TeamEnum.myTeam;
        } else if (team1Score < team2Score) {
            return TeamEnum.otherTeam;
        } else {
            return TeamEnum.none;
        }
    }

    private void endMatch(TeamEnum winner, Game game) {
        //TODO GÖr så att alla skicknings försök görs i separata try catch
        State state = engine.getState();
        Info info = new Info();

        info.setWinner(winner);

        state.setInfo(info);

        sendState(state, game, true);

        team1.setClientState(ClientState.READY);
        team2.setClientState(ClientState.READY);
        observerServer.endGame();
        this.winner = winner;
    }

    public void abortMatch() throws CageballMatchException {
        running = false;
    }

    private void sendState(State state, Game game) {
        sendState(state, game, false);
    }

    private void sendState(State state, Game game, boolean last) {

        if (last) {
            if (gameType == GameType.Tournament) {
                game.setWinner(winner);
                game.setTeam1Score(team1Score);
                game.setTeam2Score(team2Score);
                scoreBoard.update(game);
                state.getInfo().setScoreBoard(scoreBoard.getScoreBoard());
            }
        }

        state.getMyTeam().setScore(team1Score);
        state.getOtherTeam().setScore(team2Score);
        try {
            team1.writeState(state);
        } catch (CageballIOException ex) {
            gameServer.removeClient(team1);
        }
        try {
            team2.writeState(inverter.invertState(state));
        } catch (CageballIOException ex) {
            gameServer.removeClient(team2);
        }

        observerServer.sendState(state);
    }
}
